package com.enlorenz.common.geo.map.google;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import com.enlorenz.common.geo.base.GeoPoint;
import com.enlorenz.common.geo.base.IPoint;
import com.enlorenz.common.geo.base.IRail;
import com.enlorenz.common.geo.map.base.MapConfig;
import com.enlorenz.common.geo.map.base.TileWrapper;
import com.enlorenz.common.geo.map.callback.IDownloadResultCallback;
import com.enlorenz.common.geo.util.GeographyUtil;
import com.enlorenz.common.geo.util.GeographyUtil.PointRect;
import com.enlorenz.core.engine.exception.EngineControlException;
import com.enlorenz.core.engine.imp.thread.quick.AbstractQuickSleepFirstThread;
import com.enlorenz.util.DataDetUtil;
import com.enlorenz.util.FileUtil;
import com.enlorenz.util.TimeUtil;

/**
 * Google瓦片算法
 * @author lcb
 */
public class GoogleTileParser {
	//地图配置文件
	private MapConfig mapConfig;
	//点矩形
	private PointRect pointRect;
	//围栏
	private IRail rail;
	//步进
	private Map<Integer,Double> stepMap=new HashMap<Integer,Double>();
	{
		stepMap.put(18, 0.0002);
		stepMap.put(17, 0.0005);
		stepMap.put(16, 0.001);
		stepMap.put(15, 0.004);
		stepMap.put(14, 0.005);
		stepMap.put(13,0.02);
		stepMap.put(12,0.05);
	}
	public void setMapConfig(MapConfig mapConfig) {
		this.mapConfig = mapConfig;
		String [] stepArr=mapConfig.getStep().split(":");
		for(String step:stepArr){
			String [] sArr=step.split(",");
			stepMap.put(Integer.valueOf(sArr[0]),Double.valueOf(sArr[1]));
		}
		
	}
	//18 0.003  17 0.005 16 0.01 15 0.02 14 0.04 13 0.08 12 0.18
	//结果数据的回调函数
	private GoogleTileHooker googleTileHooker;
	private HashMap<String,TileWrapper> map=new HashMap<String,TileWrapper>();
	private Properties progressProperties;
	private static final String PROP_FILE_PATH="cache.properties";
	
	public HashMap<String, TileWrapper> getMap() {
		return map;
	}
	
	public GoogleTileParser(GoogleTileHooker googleTileHooker){
		this.googleTileHooker=googleTileHooker;
	}
	
	public GoogleTileParser(){
	}
	
	/**
	 * 结果数据的回调函数
	 * @author lcb
	 *
	 */
	public static interface GoogleTileHooker{
		void hook(long totalNum,int curNum,TileWrapper googleTileWrapper);
	}
	//是否解析完毕
	private boolean parseOver=false;
	private String makeKey(int zoom){
		return pointRect+"-"+zoom;
	}
	private AbstractQuickSleepFirstThread savePropertiesThread;
	private void runSavePropThread(){
		savePropertiesThread=new AbstractQuickSleepFirstThread() {
			@Override
			protected void handle() throws Exception {
				FileUtil.saveProperties(progressProperties, PROP_FILE_PATH);
			}
		};
		
		savePropertiesThread.setName("SavePropertiesThread");
		savePropertiesThread.setSleepTime(TimeUtil.SECOND_MILLI*8);
		try {
			savePropertiesThread.init();
			savePropertiesThread.start();
		} catch (EngineControlException e) {
			e.printStackTrace();
		}
		mapConfig.display("@ 文件保存线程：["+savePropertiesThread.getName()+"]");
	}
	
	public AbstractQuickSleepFirstThread getSavePropertiesThread() {
		return savePropertiesThread;
	}

	public void doParse(){
		//初始化区域
		initArea();
		//地图级别
		int [] zoom=mapConfig.getZoom();
		//读取进度保存文件
		loadPropFile();
		//启动文件保存线程
		runSavePropThread();
		
		mapConfig.display("@ 是否覆盖图片：["+mapConfig.isCover()+"]");
		mapConfig.display("@ 获取层级数组："+Arrays.toString(zoom));
		
		for(int z:zoom){
			mapConfig.display("**********************【Start-Zoom["+z+"]】************************");
			runZoom(z);
			mapConfig.display("**********************【End-Zoom["+z+"]】************************");
		}
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		mapConfig.display("**********所有的坐标已经分析完成**********");
		parseOver=true;
	}
	
	/**
	 * 读取进度保存文件
	 */
	private void loadPropFile(){
		mapConfig.display("@ 进度保存文件：["+PROP_FILE_PATH+"]");
		try {
			progressProperties=FileUtil.getProperties(PROP_FILE_PATH);
		} catch (Exception e1) {
			e1.printStackTrace();
		}
	}
	
	private void runZoom(int zoom){
		mapConfig.display("@ 获取步进Map：["+stepMap+"]");
		//步进
		Double step=stepMap.get(zoom);
		if(null == step){
			step=stepMap.get(12);
		}
		mapConfig.display("@ 当前步进级别：["+step+"]");
		
		long totalNum=(long)((getRealNum((pointRect.getLonEd()-pointRect.getLonSt())/step))*
				(getRealNum((pointRect.getLatEd()-pointRect.getLatSt())/step)));
		mapConfig.display("@ 显示执行参数：[地图级别("+zoom+"),地图步进("+step+"),坐标总数("+totalNum+")]");
		long indexX=0;
		int allCount=0;
		
		
		double lonIndex=pointRect.getLonSt();
		double latIndex=pointRect.getLatSt();
		String pKey=makeKey(zoom);
		try {
			String val=progressProperties.getProperty(pKey);
			if(!DataDetUtil.isStrEmpty(val)){
				String [] arr=val.split(",");
				lonIndex=Double.valueOf(arr[0]);
				latIndex=Double.valueOf(arr[1]);
				allCount=Integer.valueOf(arr[2]);
				mapConfig.display("@ 读取进度文件：[从("+lonIndex+","+latIndex+")开始处理-Zoom("+zoom+")]");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		mapConfig.display("@ 文件输入目录：["+mapConfig.getSrcFilePath()+"]");
		mapConfig.display("@ 文件输出目录：["+mapConfig.getDstFilePath()+"]");
		
		try {
			Thread.sleep(1200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		for(double i=lonIndex;i<=pointRect.getLonEd();i+=step){
			indexX++;
			long indexY=0;
			for(double j=latIndex;j<=pointRect.getLatEd();j+=step){
				progressProperties.put(pKey, i+","+j+","+allCount);
				
				allCount++;
				indexY++;
				mapConfig.progress(allCount, totalNum);
				
				if(null!=rail){
					if(isInArea(rail, new GeoPoint(i,j))){
						getMap(zoom, i, j,indexX,indexY,totalNum,allCount);
					}else{
						mapConfig.downloadResult("main", IDownloadResultCallback.RESULT_TYPE_FAILED_NOT_IN＿AREA, 
								"["+i+","+j+"]不在范围内", allCount, totalNum, null, null,0);
					}
				}else{
					getMap(zoom, i, j,indexX,indexY,totalNum,allCount);
				}
			}
		}
	}
	
	
	private PointRect createPr(){
		List<IPoint> pList=new ArrayList<IPoint>();
		pList.add(new GeoPoint(119.948730,30.401306));
		pList.add(new GeoPoint(120.319519,30.403675));
		pList.add(new GeoPoint(120.353851,30.183121));
		pList.add(new GeoPoint(120.088806,30.178373));
		return GeographyUtil.getPointListRect(pList);
	}
	
	protected boolean isInArea(IRail rail,IPoint point){
		return GeographyUtil.isInPointRect(point, pointRect);
	}
	
	
	private static Double getRealNum(Double input){
		double num=Math.floor(input);
		if(num == input){
			return num;
		}else{
			return num+1;
		}
	}
	
	/**
	 * 清除数据
	 */
	private void clear(){
		//set.clear();
	}
	
	private static final String KEY_SPLIT="_";
	/**
	 * 创建结果的唯一主键
	 * @param n
	 * @param xtile
	 * @param ytile
	 * @return
	 */
	private String createKey(int n,int xtile,int ytile){
		return n+KEY_SPLIT+xtile+KEY_SPLIT+ytile;
	}
	
	private void getMap(int zoom,double lon_deg,double lat_rad,long x,long y,long totalNum,int allCount){
		int n = zoom;
		int xtile = long2tile(lon_deg, n);
		int ytile = lat2tile(lat_rad, n);
		//String key=createKey(n, xtile, ytile);
		/*if(null != map.get(key)){
			return;
		}*/
		
		TileWrapper googleTileWrapper=new TileWrapper();
		googleTileWrapper.setZoom(String.valueOf(zoom));
		googleTileWrapper.setXtile(String.valueOf(xtile));
		googleTileWrapper.setYtile(String.valueOf(ytile));
		googleTileWrapper.setX(x);
		googleTileWrapper.setY(y);
		//map.put(key, googleTileWrapper);
		if(null!=googleTileHooker){
			googleTileHooker.hook(totalNum,allCount,googleTileWrapper);
		}
	}
	
	private double sec(double lat_rad){
		return 1/Math.cos(lat_rad);
	}
	
	private int long2tile(double lon,double zoom1) { 
		return (int)(Math.floor((lon+180)/360*Math.pow(2,zoom1)));
	}
	
	private int lat2tile(double lat,double zoom2)  { 
		return (int)(Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom2))); 
	}
	
	
	
	/**
	 * 初始化范围
	 */
	public void initArea() {
		List<IPoint> pointList=mapConfig.getPointList();
		//计算点矩形
		pointRect=GeographyUtil.getPointListRect(pointList);
		
		mapConfig.display("@ 获取最大范围：["+pointRect.getLonSt()
				+","+pointRect.getLonEd()+","+pointRect.getLatSt()+","+pointRect.getLatEd()+"]");
		rail=new IRail(){
			private List<IPoint> list;
			private IPoint rightPoint;
			@Override
			public String getRailAocId() {
				return null;
			}

			@Override
			public String getRailId() {
				return null;
			}

			@Override
			public String getRailName() {
				return null;
			}

			@Override
			public List<IPoint> getRailPointList() {
				return list;
			}

			@Override
			public IPoint getRightPoint() {
				return rightPoint;
			}

			@Override
			public void setRailPointList(List<IPoint> list) {
				this.list=list;
			}

			@Override
			public void setRightPoint(IPoint rightPoint) {
				this.rightPoint=rightPoint;
			}
			
		};
		IPoint rightPoint=GeographyUtil.getRightPointFromList(pointList);
		rail.setRailPointList(pointList);
		rail.setRightPoint(rightPoint);
	}
	
	public boolean isParseOver() {
		return parseOver;
	}

	
	public void setGoogleTileHooker(GoogleTileHooker googleTileHooker) {
		this.googleTileHooker = googleTileHooker;
	}
	
}
